package com.hejing.线程间通信与生产者消费者模型;

/**
 * ClassName: ProducerConsumerTest
 * Package: com.hejing.线程间通信与生产者消费者模型
 * Description:
 *  案例描述:
 *      生产者(Producer)将产品交给店员(Clerk)，而消费者(Consumer)从店员处取走产品，店员一次只能持有固定数量的产品
 *      (比如:20)，如果生产者试图生产更多的产品，店员会叫生产者停一下， 如果店中有空位放产品了再通知生产者继续生产；如
 *      果店中没有产品了，店员会告诉消费者等一下， 如果店中有产品了再通知消费者来取走产品。
 *
 *  案例分析:
 *      1、是否是多线程问题: 是 (生产者消费者)
 *      2、是否有共享数据: 有 (产品)
 *      3、是否存在线程安全问题: 有 (存在线程操作共享数据的情况)
 *      4、如何处理线程安全问题: 使用同步机制
 *      5、是否存在线程间通信: 是 (最大产品值20)
 *
 * @Author: HeJing
 * @Create: 2023-04-15 19:10
 * @Version: 1.0
 */
public class ProducerConsumerTest {
    public static void main(String[] args) {

        Clerk clerk = new Clerk();  // 这个对象就是共享数据

        Producer pro1 = new Producer(clerk);
        Consumer con1 = new Consumer(clerk);
        Consumer con2 = new Consumer(clerk);

        pro1.setName("生产者1");
        con1.setName("消费者1");
        con2.setName("消费者2");

        pro1.start();
        con1.start();
        con2.start();
    }
}


/**
 * 店员
 */
class Clerk {
    private int productNum = 0;  // 产品的数量

    // 提供增加产品数量的方法
    public synchronized void addProduct() {  // 非静态方法默认同步监视器是 this

        if (productNum >= 20) {
            // 等待
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        } else {
            productNum++;
            System.out.println(Thread.currentThread().getName() + "生产了第" + productNum + "个产品");

            // 已添加新产品，唤醒消费者
            notifyAll();
        }
    }
    // 提供减少产品数量的方法
    public synchronized void minusProduct() {
        if (productNum <= 0) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        } else {
            System.out.println(Thread.currentThread().getName() + "消费了第" + productNum + "个产品");
            productNum--;

            // 已消费了产品，唤醒生产者
            notify();
        }

    }
}

/**
 * 生产者
 */
class Producer extends Thread {
    private Clerk clerk;

    public Producer(Clerk clerk) {
        this.clerk = clerk;
    }

    @Override
    public void run() {
        while(true) {
            System.out.println("生产者开始生产产品...");
            try {
                Thread.sleep(45);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            clerk.addProduct();
        }
    }
}

/**
 * 消费者
 */
class Consumer extends Thread {

    private Clerk clerk;

    public Consumer(Clerk clerk) {
        this.clerk = clerk;
    }
    @Override
    public void run() {
        while(true) {
            System.out.println("消费者开始消费产品...");
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            clerk.minusProduct();
        }
    }
}



















